<?php
/*
 * --------------------------------------------------------------
 *   NewProductsFilter.php 2023-06-14
 *   Gambio GmbH
 *   http://www.gambio.de
 *   Copyright (c) 2023 Gambio GmbH
 *   Released under the GNU General Public License (Version 2)
 *   [http://www.gnu.org/licenses/gpl-2.0.html]
 * --------------------------------------------------------------
 */

declare(strict_types=1);

namespace Gambio\Shop\Modules\ProductListing\App\Filters;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Result;
use Gambio\Core\Configuration\Services\ConfigurationFinder;
use Gambio\Shop\Modules\ProductListing\App\Event\NewProductsCollected;
use Gambio\Shop\Modules\ProductListing\Model\Collections\ListingItemIds;
use Gambio\Shop\Modules\ProductListing\Model\Listing;
use Gambio\Shop\Modules\ProductListing\Model\ValueObjects\ListingItemId;
use Gambio\Shop\Modules\ProductListing\Model\ValueObjects\ListingSettings;
use Gambio\Shop\Modules\ProductListing\Model\ValueObjects\ListingSortDirection;
use Gambio\Shop\Modules\ProductListing\Model\ValueObjects\ListingSortOrder;
use Gambio\Shop\Modules\ProductListing\Model\ValueObjects\ListingSortValue;
use Gambio\Shop\Modules\ProductListing\Service\ListingFilter;

/**
 * Class NewProductsFilter
 *
 * @package Gambio\Shop\Modules\ProductListing\App\Filters
 */
class NewProductsFilter implements ListingFilter
{
    use FilterUtilityTrait;
    
    private ListingSettings     $settings;
    private Connection          $connection;
    private ConfigurationFinder $configurationFinder;
    
    
    public function __construct(
        ListingSettings     $settings,
        Connection          $connection,
        ConfigurationFinder $configurationFinder
    ) {
        $this->settings            = $settings;
        $this->connection          = $connection;
        $this->configurationFinder = $configurationFinder;
    }
    
    
    /**
     * @inheritDoc
     */
    public function getProductIds(): ListingItemIds
    {
        $cb = static function (array $item): ListingItemId {
            return new ListingItemId((int)$item['products_id']);
        };
        
        $result = $this->buildStatement();
        
        return new ListingItemIds(...array_map($cb, $result->fetchAllAssociative()));
    }
    
    
    /**
     * @inheritDoc
     */
    public function getSortOrder(): ListingSortOrder
    {
        return new ListingSortOrder(ListingSortValue::id(), ListingSortDirection::asc());
    }
    
    
    /**
     * @inheritDoc
     */
    public function getSettings(): ListingSettings
    {
        return $this->settings;
    }
    
    
    /**
     * @inheritDoc
     */
    public function getListingEvent(Listing $listing): ?object
    {
        return new NewProductsCollected($listing);
    }
    
    
    /**
     * Builds a sql statement fetching ids for products that should appear as new.
     * This method also takes group and fsk checks into account.
     *
     * @return Result
     * @see \NewProductsMainThemeContentView::build_sql_query()
     *
     */
    private function buildStatement(): Result
    {
        $qb = $this->connection->createQueryBuilder()->select('p.products_id')->from('products', 'p');
        
        $this->prepareDefaultStatement($qb, $this->settings, $this->configurationFinder, $this->connection);
        
        $qb->andWhere($qb->expr()->gt('p.products_date_added',
                                      $qb->expr()->literal($this->getNewProductsDate())));
        
        $qb->orderBy('RAND()');
        
        return $qb->executeQuery();
    }
    
    
    /**
     * Get SQL suitably formatted date of the earliest date to consider product "new"
     *
     * @return string
     */
    private function getNewProductsDate(): string
    {
        return date('Y-m-d',
                    mktime(1,
                           1,
                           1,
                           (int)date('m'),
                           date('d') - $this->getMaxDisplayNewProductsDays(),
                           (int)date('Y')));
    }
    
    
    /**
     * @return int
     */
    private function getMaxDisplayNewProductsDays(): int
    {
        return (int)$this->configurationFinder->get('configuration/MAX_DISPLAY_NEW_PRODUCTS_DAYS');
    }
}